{/* Copyright 2020 Adobe. All rights reserved.
This file is licensed to you under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. You may obtain a copy
of the License at http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under
the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
OF ANY KIND, either express or implied. See the License for the specific language
governing permissions and limitations under the License. */}

import {Layout} from '@react-spectrum/docs';
export default Layout;

import docs from 'docs:@react-aria/dialog';
import overlaysDocs from 'docs:@react-aria/overlays';
import focusDocs from 'docs:@react-aria/focus';
import {HeaderInfo, FunctionAPI, TypeContext, InterfaceType, TypeLink, PageDescription} from '@react-spectrum/docs';
import {Keyboard} from '@react-spectrum/text';
import packageData from '@react-aria/dialog/package.json';
import Anatomy from './anatomy.svg';
import ChevronRight from '@spectrum-icons/workflow/ChevronRight';
import {ExampleCard} from '@react-spectrum/docs/src/ExampleCard';
import tailwindPreview from 'url:./tailwind.png';

---
category: Overlays
keywords: [overlays, dialog, modal, aria]
---

# useDialog

<PageDescription>{docs.exports.useDialog.description}</PageDescription>

<HeaderInfo
  packageData={packageData}
  componentNames={['useDialog']}
  sourceData={[
    {type: 'W3C', url: 'https://www.w3.org/WAI/ARIA/apg/patterns/dialogmodal/'}
  ]} />

## API

<FunctionAPI function={docs.exports.useDialog} links={docs.links} />

## Features

The HTML [&lt;dialog&gt;](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dialog) element
can be used to build dialogs. However, it is not yet widely supported across browsers, and
building fully accessible custom dialogs from scratch is very difficult and error prone.
`useDialog` helps achieve accessible dialogs that can be styled as needed.

* **Flexible** – Dialogs can be used within a [modal](../Modal/useModalOverlay.html) or [popover](../Popover/usePopover.html) to create many types of overlay elements.
* **Accessible** – Exposed to assistive technology as a `dialog` or `alertdialog` with ARIA. The dialog is labeled by its title element, and content outside the dialog is hidden from assistive technologies while it is open.
* **Focus management** – Focus is moved into the dialog on mount, and restored to the trigger element on unmount. While open, focus is contained within the dialog, preventing the user from tabbing outside.

## Anatomy

<Anatomy />

A dialog consists of a container element and an optional title. `useDialog` handles
exposing this to assistive technology using ARIA. It can be combined
with <TypeLink links={overlaysDocs.links} type={overlaysDocs.exports.useModalOverlay} /> or <TypeLink links={overlaysDocs.links} type={overlaysDocs.exports.usePopover} />,
to create modal dialogs, popovers, and other types of overlays.

`useDialog` returns props that you should spread onto the appropriate element:

<TypeContext.Provider value={docs.links}>
  <InterfaceType properties={docs.links[docs.exports.useDialog.return.id].properties} />
</TypeContext.Provider>

If a dialog does not have a visible title element, an `aria-label` or `aria-labelledby`
prop must be passed instead to identify the element to assistive technology.

## Example

This example shows how to build a typical modal dialog, by combining `useDialog` with [useModalOverlay](../Modal/useModalOverlay.html). The code for the `Modal` component is available below. The `Dialog` component may also be used within a [popover](../Popover/usePopover.html). See the docs for more details on overlay containers.

```tsx example
import type {AriaDialogProps} from '@react-aria/dialog';
import {useDialog} from '@react-aria/dialog';

// Reuse the Button and Modal from your component library. See below for details.
import {Button, Modal, ModalTrigger} from 'your-component-library';

interface DialogProps extends AriaDialogProps {
  title?: React.ReactNode,
  children: React.ReactNode
}

function Dialog({title, children, ...props}: DialogProps) {
  let ref = React.useRef(null);
  let {dialogProps, titleProps} = useDialog(props, ref);

  return (
    <div {...dialogProps} ref={ref} style={{padding: 30}}>
      {title &&
        <h3 {...titleProps} style={{marginTop: 0}}>
          {title}
        </h3>
      }
      {children}
    </div>
  );
}

<ModalTrigger label="Open Dialog">
  {close =>
    <Dialog title="Enter your name">
      <form style={{display: 'flex', flexDirection: 'column'}}>
        <label htmlFor="first-name">First Name:</label>
        <input id="first-name" />
        <label htmlFor="last-name">Last Name:</label>
        <input id="last-name" />
        <Button
          onPress={close}
          style={{marginTop: 10}}>
          Submit
        </Button>
      </form>
    </Dialog>
  }
</ModalTrigger>
```

### Modal

The `Modal` and `ModalTrigger` components render the dialog within a typical modal container with a partially transparent underlay. See [useModalOverlay](../Modal/useModalOverlay.html) for more details.

<details>
  <summary style={{fontWeight: 'bold'}}><ChevronRight size="S" /> Show code</summary>

```tsx example export=true render=false
import {useOverlayTriggerState} from '@react-stately/overlays';
import {Overlay, useModalOverlay, useOverlayTrigger} from '@react-aria/overlays';
import {useViewportSize} from '@react-aria/utils';

function Modal({state, children, ...props}) {
  let ref = React.useRef(null);
  let {modalProps, underlayProps} = useModalOverlay(props, state, ref);

  return (
    <Overlay>
      <div
        style={{
          position: 'absolute',
          zIndex: 100,
          top: 0,
          left: 0,
          width: '100%',
          height: document.body.clientHeight,
          background: 'rgba(0, 0, 0, 0.5)'
        }}
        {...underlayProps} />
      <div
        style={{
          position: 'fixed',
          top: 0,
          left: 0,
          width: '100%',
          height: useViewportSize().height + 'px',
          zIndex: 101,
          display: 'flex',
          alignItems: 'center',
          justifyContent: 'center'
        }}>
        <div
          {...modalProps}
          ref={ref}
          style={{
            background: 'var(--page-background)',
            border: '1px solid gray'
          }}>
          {children}
        </div>
      </div>
    </Overlay>
  );
}

function ModalTrigger({label, children, ...props}) {
  let state = useOverlayTriggerState(props);
  let {triggerProps, overlayProps} = useOverlayTrigger({type: 'dialog'}, state);

  return <>
    <Button {...triggerProps}>{label}</Button>
    {state.isOpen &&
      <Modal state={state}>
        {React.cloneElement(children(state.close), overlayProps)}
      </Modal>
    }
  </>;
}
```

</details>

### Button

The `Button` component is used in the above example to open and close the dialog. It is built using the [useButton](../Button/useButton.html) hook, and can be shared with many other components.

<details>
  <summary style={{fontWeight: 'bold'}}><ChevronRight size="S" /> Show code</summary>

```tsx example export=true render=false
import {useButton} from '@react-aria/button';

function Button(props) {
  let ref = React.useRef(null);
  let {buttonProps} = useButton(props, ref);
  return <button {...buttonProps} ref={ref} style={props.style}>{props.children}</button>;
}
```

</details>

## Styled examples

<ExampleCard
  url="https://codesandbox.io/s/delicate-hill-j4p5vs?file=/src/AlertDialog.tsx"
  preview={tailwindPreview}
  title="Tailwind CSS"
  description="An animated alert dialog using Tailwind and react-transition-group." />
